// 运算时文件
export class Realm {
  constructor() {
    this.golbal = new Map(),
    this.Object = new Map(),
    this.Object.call = function() { // 还包含其他属性 如：execute
    },
    this.Object_prototype = new Map()
  }
}

// export class EnvironmentRecord {
//   constructor() {
//     this.thisValue
//     this.variables = new Map()
//     this.outer = null
//   }
// }

export class EnvironmentRecord { // 作用域
  constructor(outer) {
    this.outer = outer // 父环境(外层作用域)
    this.variables = new Map
  }
  add(name) {
    this.variables.set(name, new JSUndefined)
  }
  get(name) {
    if (this.variables.has(name)) { // 先判断自身有没有，没有就去外层找
      return this.variables.get(name)
    } else if (this.outer) {
      return this.outer.get(name)
    } else {
      return new JSUndefined
    }
  }
  set(name, value = new JSUndefined) {
    if (this.variables.has(name)) { // 先判断自身有没有，没有就去外层找
      return this.variables.set(name, value)
    } else if (this.outer) {
      return this.outer.set(name, value)
    } else {
      return this.variables.set(name, value)
    }
  }
}

export class ObjectEnvironmentRecord {
  constructor(object, outer) {
    this.object = object
    this.outer = outer // 父环境(外层作用域)
  }
  add(name) {
    this.object.set(name, new JSUndefined)
  }
  get(name) {
    return this.object.get(name)
    // TODO:with statement need outer
  }
  set(name, value = new JSUndefined) {
    this.object.set(name, value)
    // TODO:with statement need outer
  }
}

export class ExecutionContext { // 包含存储变量的方法以及 prototype 、var
  // LexicalEnvironment：{ a:1, b:2} // 词法环境 lexical就是lexer所依赖的环境
  // global的object 包含三个对象 global对象、Object构造器、Object的原型对象
  constructor(realm, lexicalEnvironment, variableEnvironment) {
    variableEnvironment = variableEnvironment || lexicalEnvironment
    this.lexicalEnvironment = lexicalEnvironment // 这也不会是一个普通的对象 所以有一个EnvironmentRecord
    this.variableEnvironment = variableEnvironment
    this.realm = realm
    // golbal: {},
    // Object: {},
    // Object_prototype: {}
  }
}

export class Reference { // Reference数据类型(对象相关)
  constructor(object, property) {
    this.object = object
    this.property = property
  }
  get() {
    // return this.object[this.property]
    return this.object.get(this.property)
  }
  set(value) {
    // this.object[this.property] = value
    this.object.set(this.property, value)
  }
}

export class CompletionRecord { // 完成记录类型(语句执行完会产生一个记录)
  constructor(type, value, target) {
    this.type = type || 'normal'
    this.value = value || new JSUndefined
    this.target = target || null
  }
}

// 进行类型转换
// number string boolean object null undefined symbol 七大基本类型  
export class JSValue { // 基类
  get type() { // 会拿到每个具体的类型， 默认给 undefined
    if (this.constructor === JSNumber) {
      return 'number'
    }
    if (this.constructor === JSString) {
      return 'string'
    }
    if (this.constructor === JSBoolean) {
      return 'boolean'
    }
    if (this.constructor === JSObject) {
      return 'object'
    }
    if (this.constructor === JSNull) {
      return 'null'
    }
    if (this.constructor === JSSymbol) {
      return 'symbol'
    }
    return 'undefined'
  }
}
export class JSNumber extends JSValue { // 
  constructor(value) {
    super()
    this.memory = new ArrayBuffer(8)
    if(arguments.length) // 浮点数
      new Float64Array(this.memory)[0] = value; // 把值存起来
    else
      new Float64Array(this.memory)[0] = 0;
  }
  get value() {
    return new Float64Array(this.memory)[0];
  }
  // 补充类型转换规则
  // toString() { // 比较麻烦，只能转换为十进制 }
  // toInt() { // 如果有位运算 }
  toNumber() {
    return this;
  }
  toBoolean() {
    if (new Float64Array(this.memory)[0] === 0) {
      return new JSBoolean(false)
    } else {
      return new JSBoolean(true)
    }
  }
  // toObject() {}

}
export class JSString extends JSValue { // 
  constructor(characters) { // 按照utf-16存起来
    super()
    // this.memory = new ArrayBuffer(characters.length * 2)
    this.characters = characters
  }
  // toNumber() {}
  toString() {
    return this;
  }
  toBoolean() {
    if (new Float64Array(this.memory)[0] === 0) {
      return new JSBoolean(false)
    } else {
      return new JSBoolean(true)
    }
  }
}
export class JSBoolean extends JSValue { // 
  constructor(value) {
    super()
    this.value = value || false
  }
  toNumber() {
    if (this.value)
      return new JSNumber(1);
    else
      return new JSNumber(0);
  }
  toString() {
    if (this.value)
      return new JSString(['t', 'r', 'u', 'e']);
    else
    return new JSString(['f', 'a', 'l', 's', 'e']);
  }
  toBoolean() {
    return this;
  }
}
export class JSObject extends JSValue { // 
  constructor(proto) {
    super()
    this.properties = new Map()
    this.prototype = proto || null
  }
  get(name) {
    // TODO: prototype chain && getter
    return this.getProperty(name).value
  }
  set(name, value) {
    // TODO: writable etc.
    this.setProperty(name, {
      value: value,
      writable: true,
      enumerable: true,
      configurable: true
    })
  }
  getProperty(name) { // 原型
    return this.properties.get(name)
  }
  setProperty(name, attributes) {
    this.properties.set(name, attributes)
  }
  getPrototype() {
    return this.prototype
  }
  setPrototype(proto) {
    this.prototype = proto
  }
}
export class JSNull extends JSValue { // 
  toNumber() {
    return new JSNumber(0);
  }
  toString() {
    return new JSString(['n', 'u', 'l', 'l']);
  }
  toBoolean() {
    return new JSBoolean(false);
  }
}
export class JSUndefined extends JSValue { // 
  toNumber() {
    return new JSNumber(NaN);
  }
  toString() {
    return new JSString(['u', 'n', 'd', 'e', 'f', 'i', 'n', 'e', 'd']);
  }
  toBoolean() {
    return new JSBoolean(false);
  }
}
export class JSSymbol extends JSValue { // 
  constructor(name) {
    super()
    this.name = name || ''
  }
}